/*
 * TCPSocket.cpp
 *
 *  Created on: 24.10.2011
 *      Author: niea
 */

#include <iostream>

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cstdio>
#include <fcntl.h>
#include <algorithm>

#include "constants.h"
#include "SocketTCP.h"
#include "Lock.h"

SocketTCP::SocketTCP() :
	m_fdsocket(0) {
	pthread_mutex_init(&m_mutex, NULL);
}

SocketTCP::~SocketTCP() {
	pthread_mutex_destroy(&m_mutex);
}

int SocketTCP::waitWriteEnd() {
	timeval timeout;
	timeout.tv_usec = CONST_TIMEOUT * 1000;
	timeout.tv_sec = 0;

	fd_set writeset;
	fd_set errset;
	FD_ZERO(&writeset);
	FD_SET(m_fdsocket, &writeset);

	if (select(m_fdsocket + 1, NULL, &writeset, NULL, &timeout) <= 0) {
		std::cerr << "Socket error: write timeout" << std::endl;
		return C_ERROR;
	} else {
		if (!FD_ISSET(m_fdsocket, &writeset)) {
			std::cerr << "Socket error: write error" << std::endl;
			return C_ERROR;
		}
		return C_OK;
	}
}

int SocketTCP::waitReadReady() {
	timeval timeout;
	timeout.tv_usec = CONST_TIMEOUT * 1000;
	timeout.tv_sec = 0;

	fd_set readset;
	FD_ZERO(&readset);
	FD_SET(m_fdsocket, &readset);

	if (select(m_fdsocket + 1, &readset, NULL, NULL, &timeout) <= 0) {
		//std::cerr << "Socket error: read timeout" << std::endl;
		return C_ERROR;
	} else {
		return C_OK;
	}
}

int SocketTCP::connectServer(std::string ip, size_t port) {
	m_fdsocket = socket(AF_INET, SOCK_STREAM, 0);
	if (m_fdsocket < 0) {
		std::cerr << "Socket error: socket" << std::endl;
		m_fdsocket = 0;
		return C_ERROR;
	}

	fcntl(m_fdsocket, F_SETFL, O_NONBLOCK);

	sockaddr_in serv_addr;
	serv_addr.sin_family = AF_INET;
	serv_addr.sin_port = htons(port);

	if (inet_aton(ip.c_str(), &serv_addr.sin_addr) < 0) {
		std::cerr << "Socket error: inet_aton" << std::endl;
		disconnect();
		return C_ERROR;
	}

	connect(m_fdsocket, (sockaddr*) &serv_addr, sizeof(serv_addr));
	if (waitWriteEnd() == C_ERROR) {
		std::cerr << "Socket error: connect" << std::endl;
		disconnect();
		return C_ERROR;
	}

	return C_OK;
}

int SocketTCP::disconnect() {
	if (m_fdsocket != 0) {
		close(m_fdsocket);
		m_fdsocket = 0;
	}
	return C_OK;
}

int SocketTCP::sendMessage(const std::vector<char> & msg) {
	Lock lock(&m_mutex);
	lock;

	send(m_fdsocket, &msg[0], msg.size(), 0);
	if (waitWriteEnd() == C_ERROR) {
		std::cerr << "Socket error: sendMessage" << std::endl;
	}
}

int SocketTCP::receiveMessage(std::vector<char> & msg) {
	Lock lock(&m_mutex);
	lock;

	msg.clear();
	size_t total_rc = 0;
	msg.resize(CONST_BUFFER_SIZE);

	int received = 0;
	do {
		if (waitReadReady() == C_ERROR) {
			//return C_ERROR;
			break;
		}
		received = recv(m_fdsocket, &msg[total_rc], CONST_BUFFER_SIZE, 0);
		total_rc += received;
	} while (received != 0);


	msg.resize(total_rc);
	return total_rc == 0 ? C_ERROR : C_OK;
}
